home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 2 / Gold Medal Software Volume 2 (Gold Medal) (1994).iso / prog / asm_0_m.arj / KBFIX.ASM < prev    next >
Assembly Source File  |  1984-06-26  |  33KB  |  1,044 lines

  1. page    ,132
  2. title  kbfix - 127 character buffer & screen display of ibm-pc shift status
  3. ;****************************************************************
  4. ;* module name = kbfix
  5. ;*
  6. ;* copyright(C) 1984    Skip Gilbrech
  7. ;*            90 Lexington Ave. #10-G
  8. ;*            New York, NY 10016
  9. ;*            212-685-0551
  10. ;*
  11. ;* This program may be freely copied/altered for any non-commercial
  12. ;* purpose but may not be sold or used in any way as part of any
  13. ;* profit-making venture without permission of the author.
  14. ;*
  15. ;* author = skip gilbrech
  16. ;* date written = 01/84
  17. ;*
  18. ;* environment:
  19. ;*  system = ibm pc (dos 2.0 - but should work on any version)
  20. ;*  processor = microsoft 8086 macro assembler
  21. ;*
  22. ;* This program will print out the status of the caps-, num-, and scroll-
  23. ;* lock keys on the upper right-hand corner of the ibm-pc screen in reverse-
  24. ;* video, and will beep from low to high when one of these keys is toggled on,
  25. ;* and from high to low when it is toggled off.  The display is updated after
  26. ;* every keystroke.  The program writes directly to the video ram to avoid the
  27. ;* overhead of rom-bios calls:    I found this approach resulted in a much
  28. ;* cleaner display.  The program should work with both monochrome and color 80
  29. ;* column monitors.
  30. ;*
  31. ;* The program is invoked only once and is installed through the dos
  32. ;* 'terminate but stay resident' function.
  33. ;*
  34. ;* I have incorporated an optional 127-character keyboard buffer to replace
  35. ;* the standard 15 provided by ibm.  The buffer is installed only if a '/b'
  36. ;* switch is encountered on the command line.
  37. ;*
  38. ;* Installation of the buffer also allows fixing a problem which I found
  39. ;* irritating:    control-s (pause output) and control-c (abort) only worked
  40. ;* if they were the only key in the buffer, i.e., if you typed ahead while
  41. ;* something else was happening, then changed your mind, you had to use
  42. ;* control-numlock or control-break to pause or abort.    This is understandable:
  43. ;* since bios doesn't treat control-s or control-c in any special way, dos
  44. ;* would have to scan the whole buffer to detect these keys in any but the
  45. ;* first position.  Now, if either of these keys is detected, the buffer
  46. ;* is cleared and the character is placed in the first buffer position.
  47. ;*
  48. ;* The program permanently occupies 1152 bytes when installed with the buffer;
  49. ;* without the buffer, it uses 672 bytes.
  50. ;*
  51. ;* The program should be independent of any particular rom-bios version, as
  52. ;* the keyboard interrupt routine gains control only after calling the vector
  53. ;* at the original int 9 (keyboard interrupt) location.  I have no idea about
  54. ;* this program's compatibility with other keyboard enhancement programs, as
  55. ;* I don't have any of them (although a friend tells me that Smartkey works
  56. ;* fine if it is installed AFTER this program).
  57. ;*
  58. ;* I have been using various versions of this program for several months,
  59. ;* now, and haven't run into any destructive bugs, but I can't accept
  60. ;* responsibility for any damage it may cause.    I would appreciate hearing
  61. ;* about any problems, however, at the number or address above, or on
  62. ;* Compuserve (71445,534).
  63. ;*
  64. ;* This ideas (and some of the code) for this program come from a variety
  65. ;* of public-domain sources, and the program evolved slowly over several
  66. ;* months, so if I miss giving credit to someone, please accept my apologies.
  67. ;*
  68. ;* The ideas of printing out the key status on the upper-right hand corner of
  69. ;* the screen and intercepting the interrupt routine after it has executed
  70. ;* come from Tony A. Rhea's KEYSTAT program.  His copyright notice is
  71. ;* reproduced below:
  72. ;*
  73. ;*    KEYSTAT -- Copyright (C) 1983 Tony A. Rhea
  74. ;*    This program may be copied for non-commercial use.
  75. ;*    Adapted from KEYFLAGS (PC-World, Oct. 83, page 266) by Morton Kaplon
  76. ;*
  77. ;* The bios beep routines were taken from the KEYLOC program by:
  78. ;*
  79. ;*    John Black
  80. ;*    5225 Pooks Hill Rd. #1715 N.
  81. ;*    Bethesda MD. 20814
  82. ;*
  83. ;* The basic idea for the enlarged keyboard buffer came from a program
  84. ;* on the Compuserve IBM-PC SIG called KEYBOARD.ASM.  There was no name
  85. ;* on the file I downloaded.
  86. ;*
  87. ;****************************************************************
  88.     page
  89. ;****************************************************************
  90. ;*
  91. ;*    constants
  92. ;*
  93. ;****************************************************************
  94.  
  95. ; general equates:
  96.  
  97. false        equ    0
  98. true        equ    not false
  99.  
  100. cr        equ    0dh    ; carriage return
  101. lf        equ    0ah    ; line feed
  102. bell        equ    7    ; ascii bell
  103. ctrl_s        equ    13h    ; control-s
  104. ctrl_c        equ    3    ; control-c
  105. spc        equ    ' '     ; space, blank
  106. tab        equ    9    ; tab
  107.  
  108. ; program equates:
  109.  
  110. bytes_per_row    equ    160    ; num. bytes per row in video ram
  111. rowcaps     equ    0    ; row for cap symbol
  112. colcaps     equ    77    ; column for cap symbol
  113. rownums     equ    0    ; row for num symbol
  114. colnums     equ    78    ; column for num symbol
  115. rowscroll    equ    0    ; row for scroll symbol
  116. colscroll    equ    79    ; column for scroll symbol
  117. blank        equ    ' '     ; display this if a mode is not active
  118. capschar    equ    'C'     ; character to signify capslock state
  119. numchar     equ    'N'     ; character to signify numlock state
  120. scrollchar    equ    'S'     ; character to signify scroll lock state
  121. normal        equ    7    ; display attribute (7=normal white on black)
  122. reverse     equ    112    ; display attr. (112=reverse video - sg)
  123. blankword    equ    ((normal * 256) + blank)    ; words for video ram
  124. capsword    equ    ((reverse * 256) + capschar)    ;
  125. numword     equ    ((reverse * 256) + numchar)    ;
  126. scrollword    equ    ((reverse * 256) + scrollchar)    ;
  127.  
  128. ; dos and bios equates:
  129.  
  130. dosint        equ    21h    ; interrupt number for dos functions
  131. print_string    equ    9    ; dos print console string function
  132. get_vers    equ    30h    ; dos get version number function
  133. get_int_vec    equ    35h    ; dos 2.0 get interrupt vector function
  134. set_int_vec    equ    25h    ; dos set interrupt vector function
  135.  
  136. kb_int_no    equ    9    ; interrupt number for keyboard interrupt
  137. kb_io_int_no    equ    16h    ; interrupt number for keyboard i/o
  138. capsmask    equ    40h    ; mask for kb_flag - capslock on
  139. nummask     equ    20h    ; mask for kb_flag - numlock on
  140. scrollmask    equ    10h    ; mask for kb_flag - scrolllock on
  141.  
  142. ; hardware specific equates:
  143.  
  144. inta01        equ    21h    ; port adr. for 8259 ocw 1 and icw's 2, 3 & 4
  145. ena_irq1    equ    11111101b    ; mask-enable irq1 (keyboard) interrupt
  146. dis_irq1    equ    00000010b    ; mask-disable irq1 interrupt
  147. stat_6845    equ    03dah        ; 6845 status register
  148.  
  149. ; for beep routine:
  150.  
  151. h_cycles    equ    30
  152. h_half        equ    300
  153. l_cycles    equ    h_cycles / 3
  154. l_half        equ    h_half * 3
  155. kb_ctl        equ    61h
  156.  
  157. page
  158.  
  159. ;**************************************************************************
  160.  
  161. int_vecs    segment at 0
  162. int_vecs    ends
  163.  
  164. ;**************************************************************************
  165.  
  166. bios_data segment at 40h
  167.  
  168.     org    17h
  169. kb_flag     db    ?        ; holds state of caps- and numlock, etc.
  170.  
  171.     org    1ah            ; keyboard buffer data area
  172. bios_buffer_head    dw    ?    ; ptr to next char to be gotten
  173. bios_buffer_tail    dw    ?    ; ptr to last char entered in buffer
  174. bios_kb_buffer    dw    16 dup (?)    ; keyboard buffer
  175. bios_kb_buffer_end    label word    ; end of buffer
  176.  
  177.     org    49h
  178. crt_mode    db    ?        ; crt mode - mono/color, etc.
  179.  
  180.     org    80h
  181. bios_buffer_start    dw    ?    ; in new bios, ptr to beginning of buf
  182. bios_buffer_end     dw    ?    ; in new bios, ptr to end of buf
  183.  
  184. bios_data ends
  185.  
  186. ;**************************************************************************
  187.  
  188. monoram segment at 0b000h        ; monochrome ram
  189. monoram ends
  190.  
  191. ;**************************************************************************
  192.  
  193. colorram segment at 0b800h        ; color/graphics ram
  194. colorram ends
  195.  
  196.     page
  197. ;**************************************************************************
  198.  
  199. cseg    segment
  200.     assume cs:cseg,ds:cseg,es:nothing
  201.  
  202.     org    80h            ; command line parms
  203. cmd_ct    label    byte            ; number of chars. in command line
  204.  
  205.     org    100h            ; for .COM file
  206. entry:
  207.     jmp    init            ; ck for residency, init if not resident
  208.  
  209. last_kb_flag    db    ?        ; kb_flag when last entered routine
  210. buf_flag    db    false        ; set to true when/if new keyboard buffer
  211.                     ; is installed
  212. orig_kb_int_vec dd    ?        ; pointer to orig. kb_int service routine
  213.  
  214. ;**************************************************************************
  215. ;* This is the start of the new keyboard interrupt handler code.
  216. ;*
  217. ;* The numlock and capslock keys tend to bounce when hit quickly, and
  218. ;* this can cause another keyboard interrupt before the beeping is done
  219. ;* (resulting in unpleasant beeps) since the bios has already sent out an
  220. ;* eoi to the 8259.
  221. ;* To solve the problem, and to simplify the keyboard buffer manipulations
  222. ;* (assuming the buffer is installed), interrupts will not be re-enabled
  223. ;* before pushing the flags and calling the original routine.  When we regain
  224. ;* control, interrupts will still be disabled, so we can mask off the keyboard
  225. ;* interrupt before re-enabling other interrupts.
  226.  
  227. kb_int_hdlr proc            ; entry point of keyboard (hardware)
  228.                     ;  interrupt handler
  229.  
  230.     pushf                ; (IRET will pop 3 words off the stack)
  231.     call    cs:orig_kb_int_vec    ; kb_flag will be updated on return
  232.  
  233. ; interrupts are disabled -- mask off just the keyboard interrupt and re-enable
  234.  
  235.     push    ax            ; save AX before using
  236.     in    al,inta01        ; get ocw1 from 8259
  237.     or    al,dis_irq1        ; turn on the mask bit
  238.     out    inta01,al        ; write modified byte to 8259
  239.     sti                ; reenable ints
  240.  
  241.     push    bx            ; save other regs. used
  242.     push    cx            ;
  243.     push    dx            ;
  244.     push    si            ;
  245.     push    ds            ;
  246.     push    es            ;
  247.  
  248.     mov    ax,cs            ; now establish data addressability
  249.     mov    ds,ax            ;  off of DS
  250.     mov    ax,bios_data        ; and set ES to bios_data segment
  251.     mov    es,ax            ;
  252.  
  253.     cmp    buf_flag,false        ; see if our keyboard buffer is in use
  254.     je    kb_int1         ; if not, continue
  255.     call    get_from_bios_buf    ; else, check for char to move
  256.  
  257. kb_int1:
  258.     mov    al,es:kb_flag        ; get bios keyboard status flag
  259.  
  260. ; check capslock state - if on, show reverse-video 'C' else show blank
  261.  
  262.     mov    dx,blankword        ; assume blank to be sent
  263.     push    dx            ; save for later
  264.     push    dx            ;  and again..
  265.     test    al,capsmask        ; al contains <kb_flag>
  266.     jz    cl1            ; if bit not set, nothing to do
  267.     mov    dx,capsword        ; else send out the caps symbol
  268. cl1:
  269.     mov    bh,rowcaps        ; row for cap symbol
  270.     mov    bl,colcaps        ; column for cap symbol
  271.     call    dispchar        ;
  272.  
  273. ; check numlock state - if on, show reverse-video 'N' else show blank
  274.  
  275.     pop    dx            ; restore blankword
  276.     test    al,nummask        ; check the flag
  277.     jz    nl1            ; if bit not set, nothing to do
  278.     mov    dx,numword        ; else, send out the numlock symbol
  279. nl1:
  280.     mov    bh,rownums        ; row for num symbol
  281.     mov    bl,colnums        ; column for num symbol
  282.     call    dispchar        ;
  283.  
  284. ; check scroll lock state - if on, show reverse-video 'S' else show blank
  285.  
  286.     pop    dx            ; restore blankword
  287.     test    al,scrollmask        ; check the flag
  288.     jz    sl1            ; if bit not set, nothing to do
  289.     mov    dx,scrollword        ; else, send out the scroll lock symbol
  290. sl1:
  291.     mov    bh,rowscroll        ; row for scroll symbol
  292.     mov    bl,colscroll        ; column for scroll symbol
  293.     call    dispchar        ;
  294.  
  295.     mov    bl,last_kb_flag     ; get last kb_flag
  296.     cmp    al,bl            ; see if anything's changed
  297.     je    int_exit        ; if not, go home
  298.  
  299. ; else, check if caps-, num- or scroll-lock has changed state
  300.  
  301.     mov    dl,capsmask        ; capslock state changed?
  302.     call    chk_chg         ;
  303.     jnz    got_diff        ; if difference, check it out
  304.  
  305.     mov    dl,nummask        ; numlock state changed?
  306.     call    chk_chg         ;
  307.     jnz    got_diff        ; if difference, check it out
  308.  
  309.     mov    dl,scrollmask        ; scroll lock state changed?
  310.     call    chk_chg         ;
  311.     jz    int_exit        ; if not, return
  312.  
  313. got_diff:
  314.     jc    beep_dn         ; carry set if transition was from on to off
  315.     call    low_beep        ; otherwise, beep low then high
  316.     call    high_beep        ;
  317.     jmp short int_exit        ;
  318.  
  319. beep_dn:
  320.     call    high_beep        ; beep high then low
  321.     call    low_beep        ;
  322.  
  323. int_exit:
  324.     mov    last_kb_flag,al     ; al has current flag - store as last flag
  325.  
  326.     pop    es            ; restore saved registers
  327.     pop    ds            ;
  328.     pop    si            ;
  329.     pop    dx            ;
  330.     pop    cx            ;
  331.     pop    bx            ;
  332.  
  333. ; now disable ints and turn the keyboard back on -- interrupts will be
  334. ; re-enabled when the flags are popped during our IRET
  335.  
  336.     cli                ;
  337.     in    al,inta01        ; get ocw1 from 8259
  338.     and    al,ena_irq1        ; shut off the mask bit
  339.     out    inta01,al        ; write modified byte to 8259
  340.  
  341.     pop    ax            ;
  342.     iret                ;
  343.  
  344. kb_int_hdlr    endp
  345.  
  346. page
  347. ;**************************************************************************
  348. ;* compare current flag (in <al>) with last flag (in <bl>) using mask in <dl>
  349. ;* set zero flag if no change -- set carry flag if bit changed from 1 to 0
  350.  
  351. chk_chg proc near
  352.  
  353.     push    ax
  354.     push    bx
  355.     and    al,dl            ; isolate bit in al
  356.     and    bl,dl            ; do same for bl
  357.     cmp    al,bl            ; carry will be set if curr. flag (al) is 0
  358.                     ; while last flag (bl) was 1
  359.     pop    bx
  360.     pop    ax
  361.     ret
  362.  
  363. chk_chg endp
  364.  
  365. ;**************************************************************************
  366. ;* display char/attrib. in dl/dh at row/column in bh/bl of video ram page 0
  367. ;* supports b/w or color monitors, but row/column won't be right on 40
  368. ;* column displays
  369. ;* on entry, ES points to bios_data seg - all registers preserved
  370.  
  371. dispchar proc near
  372.  
  373.     push    ax            ; save registers used
  374.     push    bx            ;
  375.     push    di            ;
  376.     push    es            ; save extra seg
  377.  
  378.     mov    ax,bytes_per_row    ; num. bytes per row in video ram
  379.     mul    bh            ; calc. row offset
  380.     sub    bh,bh            ; make BH = 0
  381.     shl    bl,1            ; col * 2 = column offset
  382.     add    ax,bx            ; AX = offset into video ram
  383.     mov    di,ax            ; move offset into DI
  384.  
  385.     cmp    es:crt_mode,7        ; 7 = mono. display
  386.     jne    co_80            ; no, then jump to color routine
  387.     mov    ax,monoram        ; set ES to mono. ram segment
  388.     mov    es,ax            ;
  389.     mov    ax,dx            ; get char/attrib in AX
  390.     stosw                ; place in video ram
  391.  
  392. dc_exit:
  393.     pop    es            ; restore extra segment
  394.     pop    di            ;
  395.     pop    bx            ;
  396.     pop    ax            ;
  397.     ret
  398.  
  399. ; -- display char. on color monitor.  DX has char/attrib., DI points to
  400. ;    offset in video ram.  Must wait for horizontal retrace to avoid screwing
  401. ;    up the display.
  402.  
  403. co_80:
  404.     mov    ax,colorram        ; set ES to color ram segment
  405.     mov    es,ax            ;
  406.     mov    bx,dx            ; save char/attrib. in BX
  407.     mov    dx,stat_6845        ; 6845 status reg port in DX
  408.  
  409. co_80_1:
  410.     in    al,dx            ; get 6845 status port
  411.     test    al,1            ; test bit 0 (on = horiz. retrace active)
  412.     jnz    co_80_1         ; loop until inactive
  413.     cli                ; no ints while waiting or writing
  414.  
  415. co_80_2:
  416.     in    al,dx            ; get status again
  417.     test    al,1            ; test for retrace active
  418.     jz    co_80_2         ; no? loop to wait until active
  419.  
  420.     mov    ax,bx            ; get char/attrib. into AX
  421.     stosw                ; place in video ram
  422.     sti                ; re-enable interrupts
  423.  
  424.     mov    dx,bx            ; restore entering value of DX
  425.     jmp    dc_exit         ; and return
  426.  
  427. dispchar endp
  428.  
  429. ;**************************************************************************
  430. ;* from KEYLOC
  431.  
  432. low_beep proc near
  433.  
  434.     push    dx
  435.     push    bx
  436.     mov    bx,l_cycles
  437.     mov    dx,l_half
  438.     call    beep
  439.     pop    bx
  440.     pop    dx
  441.     ret
  442.  
  443. low_beep endp
  444.  
  445. high_beep proc near
  446.  
  447.     push    dx
  448.     push    bx
  449.     mov    bx,h_cycles
  450.     mov    dx,h_half
  451.     call    beep
  452.     pop    bx
  453.     pop    dx
  454.     ret
  455.  
  456. high_beep endp
  457.  
  458. ;**************************************************************************
  459. ;* Adapted from BIOS Beep routine
  460. ;
  461. ;  bx = # of cycles
  462. ;  dx = length of half cycle
  463.  
  464. beep proc near
  465.  
  466.     push    ax
  467.     push    cx
  468.     in    al,kb_ctl
  469.     push    ax
  470. k65:
  471.     and    al,0FCh
  472.     out    kb_ctl,al
  473.     mov    cx,dx
  474. k66:    loop    k66
  475.     or    al,2
  476.     out    kb_ctl,al
  477.     mov    cx,dx
  478. k67:    loop    k67
  479.     dec    bx
  480.     jnz    k65
  481.     pop    ax
  482.     out    kb_ctl,al
  483.     pop    cx
  484.     pop    ax
  485.     ret
  486.  
  487. beep endp
  488.  
  489. ;**************************************************************************
  490. ;* this is the end of the code which will be installed if the keyboard
  491. ;* buffer handler is NOT used -- kb_int_len will be used as the length
  492. ;* of code for the residency test in any case, since it is sufficient to
  493. ;* determine if the program has been installed in some form.
  494.  
  495. kb_int_end label near            ; area after this usable by dos if
  496.                     ;  buffer not installed
  497.  
  498. kb_int_len equ    (offset kb_int_end - offset kb_int_hdlr)
  499.  
  500.     page
  501. ;**************************************************************************
  502. ;* the following data locations will be used for our replacement keyboard
  503. ;* buffer if it is installed
  504.  
  505. our_buffer_head     dw    ?        ; ptr to next char to be gotten
  506. our_buffer_tail     dw    ?        ; ptr to last char entered in buffer
  507. our_kb_buffer        dw    128 dup (?)    ; keyboard buffer
  508. our_kb_buffer_end    label word        ; end of buffer
  509.  
  510. ; This is essentially a copy of the bios keyboard i/o interrupt handler
  511. ; modified to check our buffer for chars rather than the bios buffer
  512. ; As in the bios routine, only AX and flags are changed.
  513.  
  514. kb_io_int_hdlr    proc far
  515.  
  516.     sti                    ; reenable ints
  517.     push    bx                ;
  518.     push    si                ;
  519.     push    ds                ;
  520.     push    es                ;
  521.  
  522.     mov    bx,cs                ; est. data addressability off of DS
  523.     mov    ds,bx                ;
  524.     mov    bx,bios_data            ; and set ES to bios_data segment
  525.     mov    es,bx                ;
  526.  
  527.     or    ah,ah                ; ah=0
  528.     jz    k1                ; ascii read
  529.     dec    ah                ; ah=1
  530.     jz    k2                ; ascii status
  531.     dec    ah                ; ah=2
  532.     jz    k3                ; shift status
  533.     jmp    short int10_end         ; invalid parameter, exit
  534.  
  535. ;----- read the key to figure out what to do
  536.  
  537. k1:                        ; ascii read
  538.     sti                    ; interrupts back on during loop
  539.     nop                    ; allow an interrupt to occur
  540.     cli                    ; interrupts back off
  541.     call    ck_clr_bios_buf         ; buf cleared (by other than kb_int)?
  542.     mov    bx,our_buffer_head        ; get pointer to head of buffer
  543.     cmp    bx,our_buffer_tail        ; test end of buffer
  544.     jz    k1                ; loop until something in buffer
  545.     mov    ax,[bx]             ; get scan code and ascii code
  546.     inc    bx                ; move pointer to next position
  547.     inc    bx                ;
  548.     cmp    bx,offset our_kb_buffer_end    ; at end of buffer?
  549.     jne    k5                ; no, continue
  550.     mov    bx,offset our_kb_buffer     ; yes, reset to buf beginning
  551. k5:
  552.     mov    our_buffer_head,bx        ; store value in variable
  553.     jmp    short int10_end         ; return
  554.  
  555. ;----- ascii status
  556.  
  557. k2:
  558.     cli                    ; interrupts off
  559.     call    ck_clr_bios_buf         ; buf cleared (by other than kb_int)?
  560.     mov    bx,our_buffer_head        ; get head pointer
  561.     cmp    bx,our_buffer_tail        ; if equal (z=1) then nothing there
  562.     mov    ax,[bx]             ;
  563.     sti                    ; interrupts back on
  564.     pop    es                ; recover registers
  565.     pop    ds                ;
  566.     pop    si                ;
  567.     pop    bx                ;
  568.     ret    2                ; throw away flags
  569.  
  570. ;----- shift status
  571.  
  572. k3:
  573.     mov    al,es:kb_flag            ; get the shift status flags
  574.  
  575. int10_end:
  576.     pop    es                ;
  577.     pop    ds                ;
  578.     pop    si                ;
  579.     pop    bx                ;
  580.     iret                    ;
  581.  
  582. kb_io_int_hdlr    endp
  583.  
  584. ;**************************************************************************
  585. ;* Called from our keyboard interrupt handler after the keystroke has been
  586. ;* processed by the bios (or by whomever is handling keyboard input).
  587. ;*
  588. ;* At this point, the keyboard interrupt is disabled, so no need to disable
  589. ;* interrupts when manipulating keyboard data.
  590. ;*
  591. ;* If there is a new character available, it will be checked for control-s and
  592. ;* control-c, and if either is found, our buffer will be cleared and that char.
  593. ;* will become the 1st and only char in it.  Dos apparently checks only the
  594. ;* first position in the buffer for these two characters, and will not pause
  595. ;* or abort a command if anything else has been entered.  Of course, ctrl-break
  596. ;* and ctrl-numlock will still work, but for those who came from a cp/m
  597. ;* background, it's an annoying feature.
  598. ;*
  599. ;* On entry:  DS is set to our CS, ES points to the <bios_data> segment
  600. ;* AX,BX,SI changed.
  601.  
  602. get_from_bios_buf    proc near
  603.  
  604.     call    ck_clr_bios_buf         ;
  605.     mov    bx,offset bios_kb_buffer + 2    ; point past dummy entry
  606.     cmp    bx,es:bios_buffer_tail        ; is there a char?
  607.     je    get_from_exit            ; no, just exit
  608.     mov    es:bios_buffer_tail,bx        ; set tail = head
  609.     mov    ax,es:[bx]            ; get the char.
  610.     cmp    al,ctrl_s            ; see if control-s
  611.     jne    get_from_0            ; no, continue
  612.     call    init_our_buf            ; clear our buf
  613. get_from_0:
  614.     cmp    al,ctrl_c            ; see if control-c
  615.     jne    get_from_1            ; no, continue
  616.     call    init_our_buf            ; clear our buf
  617. get_from_1:
  618.     mov    si,our_buffer_tail        ; get ptr to next pos in buffer
  619.     mov    bx,si                ; save the value
  620.     inc    si                ; bump to next position
  621.     inc    si                ;
  622.     cmp    si,offset our_kb_buffer_end    ; wrapped?
  623.     jne    get_from_2            ; no, continue
  624.     mov    si,offset our_kb_buffer     ; else, set ptr to beginning of buf
  625.  
  626. get_from_2:
  627.     cmp    si,our_buffer_head        ; see if there's room in the buf
  628.     jne    get_from_3            ; continue if so
  629.     call    err_beep            ; if not, beep, discard data
  630.     ret                    ;
  631.  
  632. get_from_3:
  633.     mov    [bx],ax             ; store the value
  634.     mov    our_buffer_tail,si        ; store ptr to next pos in buf
  635.  
  636. get_from_exit:
  637.     ret
  638.  
  639. get_from_bios_buf    endp
  640.  
  641. ;* Check if the bios buffer has been cleared by seeing if tail is set
  642. ;* to the beginning of the buffer.  Normally, tail is equal to either
  643. ;* beginning + 2 (if no data in buf) or beginning + 4 (if data).
  644. ;* If cleared, clear our buffer and reset the bios buffer to its normal
  645. ;* condition.  At this point, interrupts are either disabled entirely,
  646. ;* or the keyboard interrupt has been masked off.
  647.  
  648. ck_clr_bios_buf     proc near
  649.  
  650.     cmp    es:bios_buffer_head,offset bios_kb_buffer  ; same?
  651.     ja    ck_clr_exit            ; if not, buf not cleared
  652.     call    init_bios_buf            ; else, reset bios buffer
  653.     call    init_our_buf            ; and reset our buffer
  654. ck_clr_exit:
  655.     ret
  656.  
  657. ck_clr_bios_buf     endp
  658.  
  659. ; set bios buffer to start 1 position higher than normal -- can then check
  660. ; for reset by seeing if head has been moved to normal starting point.
  661.  
  662. init_bios_buf proc near
  663.  
  664.     mov    bx,offset bios_kb_buffer + 2    ;
  665.     mov    es:bios_buffer_head,bx        ;
  666.     mov    es:bios_buffer_tail,bx        ;
  667.     ret
  668.  
  669. init_bios_buf endp
  670.  
  671. ; initialize our keyboard buffer
  672.  
  673. init_our_buf    proc near
  674.  
  675.     mov    si,offset our_kb_buffer     ; and reset our buffer
  676.     mov    our_buffer_head,si        ;
  677.     mov    our_buffer_tail,si        ;
  678.     ret
  679.  
  680. init_our_buf    endp
  681.  
  682. ; buffer-full beep -- essentially the same as the bios routine
  683.  
  684. err_beep    proc near
  685.  
  686.     push    bx
  687.     push    dx
  688.     mov    bx,80h            ; num. cycles for 1/12 second tone
  689.     mov    dx,48h            ; half cycle time for tone
  690.     call    beep
  691.     pop    dx
  692.     pop    bx
  693.     ret
  694.  
  695. err_beep    endp
  696.  
  697. kb_io_int_end l(FΩ⌠( è█g (:∙┼▌ê≡F≡O@@g ≡g è:<(B⌠( è█g (▌ê≡F≡≥ü@
  698.     page
  699. ;**************************************************************************
  700.  
  701. resident_msg        db    bell,'KBFIX already resident',cr,lf,'$'
  702. buf_err_msg        db    bell,'Original buffer in unknown state...',cr,lf,'$'
  703. kbfix_install_msg    db    'KBFIX installed with','$'
  704. buf_not_install_msg    db    'out'
  705. buf_install_msg     db    cr,lf,'127-byte keyboard buffer',cr,lf,'$'
  706. usage_msg        db    'Usage: kbfix /b (or) /n',cr,lf
  707.             db    '/b = install 127 char keyboard buffer',cr,lf
  708.             db    '/n = no buffer desired',cr,lf,'$'
  709. buf_desired        db    false
  710.  
  711. ;**************************************************************************
  712. ;* Initialization procedure:
  713. ;*
  714. ;*    1. Check whether the routine is already resident; if it is, then
  715. ;*       just print message and exit.
  716. ;*    2. Check command line to see if enlarged buffer installation
  717. ;*       desired.  If no parms or unknown parms, print usage message.
  718. ;*    2. Get current kb_int vector and store, then change vector to point
  719. ;*       to new interrupt handler and print message.
  720. ;*    3. If buffer installation desired, check the status of the existing
  721. ;*       keyboard buffer.
  722. ;*    4. If ok, install new keyboard i/o interrupt handler;
  723. ;*       otherwise, do NOT change the vector.
  724. ;*    5. Print message showing status of keyboard buffer.
  725. ;*    6. Execute dos 'terminate but stay resident' interrupt, installing
  726. ;*       only the code which will actually be used.
  727.  
  728. init    proc    near
  729.  
  730.     call    testresident        ; see if we are already servicing kb_int
  731.     jz    dont_install        ; ZF set = already resident -- print msg and exit
  732.  
  733.     call    get_parms        ; see if keyboard buffer desired
  734.     jc    print_usage        ; if parm error, print usage message
  735.  
  736.     ; shut off the keyboard interrupt while initializing
  737.  
  738.     cli                ; shut off interrupts while messing with 8259
  739.     in    al,inta01        ; get ocw1 from 8259
  740.     or    al,dis_irq1        ; turn on the mask bit
  741.     out    inta01,al        ; write modified byte to 8259
  742.     sti                ; reenable ints
  743.  
  744.     call    install_kb_int_hdlr    ; install new keyboard interrupt handler
  745.  
  746.     cmp    buf_desired,true    ; see if buffer installation desired
  747.     je    install_buf        ; if so, install it
  748.  
  749.     mov    dx,offset kbfix_install_msg    ; print kbfix install message
  750.     call    msg            ;
  751.     jmp    short buf_not_installed ;
  752.  
  753. install_buf:
  754.     call    install_kb_io_int_hdlr    ; install new buffer handler if ok
  755.     pushf                ; save result flags
  756.     mov    dx,offset kbfix_install_msg    ; print kbfix install message
  757.     call    msg            ;
  758.     popf                ; restore flags
  759.     jc    buf_not_installed    ; buffer not installed if carry set
  760.     mov    dx,offset buf_install_msg ; else, show buffer installation
  761.     call    msg            ;
  762.     mov    dx,offset kb_io_int_end ; set up to install everything...
  763.     jmp    short make_res        ; and install it
  764.  
  765. buf_not_installed:
  766.     mov    dx,offset buf_not_install_msg    ; else, print msg showing not installed
  767.     call    msg            ;
  768.     mov    dx,offset kb_int_end    ; install only kb_int_hdlr
  769.  
  770. make_res:
  771.     cli                ; re-enable keyboard interrupt
  772.     in    al,inta01        ; get ocw1 from 8259
  773.     and    al,ena_irq1        ; shut off the mask bit
  774.     out    inta01,al        ; write modified byte to 8259
  775.     sti                ;
  776.     int    27h            ; dos terminate but stay resident func.
  777.  
  778. dont_install:
  779.     mov    dx,offset resident_msg    ; print msg showing already installed
  780.     call    msg            ;
  781.     int    20h            ; return to dos (nothing made resident)
  782.  
  783. print_usage:
  784.     mov    dx,offset usage_msg    ;
  785.     call    msg            ;
  786.     int    20h            ; return to dos (nothing made resident)
  787.  
  788. init    endp
  789.  
  790. ;**************************************************************************
  791. ;* see if KBFIX already servicing kb_int - AX,BX,CX,SI,DI changed
  792.  
  793. testresident proc near
  794.  
  795.     push    es            ;
  796.     mov    al,kb_int_no        ; interrupt number in AL
  797.     call    get_vec         ; interrupt vector returned in ES:BX
  798.     mov    orig_kb_int_vec,bx    ; store IP of current servicer
  799.     mov    orig_kb_int_vec[2],es    ; store CS of current servicer
  800.     mov    cx,kb_int_len        ; number of bytes to match
  801.     mov    si,offset kb_int_hdlr    ; point DS:SI to our code
  802.     mov    di,bx            ; make ES:DI point to current kb_int servicer
  803.     cld                ; auto increment
  804.     repe    cmpsb            ; compare while equal - zero flag set on exit
  805.                     ; if all bytes matched (already resident)
  806.     pop    es            ;
  807.     ret
  808.  
  809. testresident endp
  810.  
  811. ;**************************************************************************
  812. ;* install new keyboard interrupt handler  - AX & DX changed
  813.  
  814. install_kb_int_hdlr    proc near
  815.  
  816.     push    es            ;
  817.     mov    ax,bios_data        ;
  818.     mov    es,ax            ; getflag expects ES = bios_data seg
  819.     mov    al,es:kb_flag        ; get bios keyboard status flag
  820.     mov    last_kb_flag,al     ; store for comparison
  821.  
  822.     mov    al,kb_int_no        ; interrupt number in AL
  823.     mov    ah,set_int_vec        ; funct. number for set int. vector
  824.     mov    dx,offset kb_int_hdlr    ; offset of resident code
  825.     int    dosint            ; set the vector
  826.     pop    es            ;
  827.     ret
  828.  
  829. install_kb_int_hdlr    endp
  830.  
  831. ;**************************************************************************
  832. ;* check status of bios keyboard buffer, install new buffer handler if ok -
  833. ;* set carry and return if not.  AX,BX,DX,SI changed.
  834. ;*
  835. ;* Buffer_start & buffer_end were not used in the original pc rom bios --
  836. ;* the offsets of kb_buffer & kb_buffer_end were hard-coded, preventing
  837. ;* easy expansion of the buffer.  The newer tech-reference manual (v2.02),
  838. ;* however, shows these variables starting at offset 80h, and bios uses them
  839. ;* as pointers to the actual keyboard buffer area, making it rather easy
  840. ;* to expand.  In my system (original bios), these locations always (as far
  841. ;* as I know) contain 0, so I will use this fact to test for unknown
  842. ;* buffer conditions.  If the locations contain 0's, a standard (16 char)
  843. ;* buffer will be assumed; if they do not, their contents will be checked
  844. ;* against the offsets of kb_buffer & kb_buffer_end.  If the values don't match,
  845. ;* the buffer has been changed somehow, and this program will not mess with it.
  846.  
  847. install_kb_io_int_hdlr    proc near
  848.  
  849.     push    es
  850.     mov    ax,bios_data            ; set ES to bios_data segment
  851.     mov    es,ax                ;
  852.  
  853.     mov    ax,es:bios_buffer_start     ; check contents of pointer
  854.     or    ax,ax                ; zero?
  855.     jz    inst_io_1            ; if so, probably old rom -
  856.                         ;  check next word
  857.     cmp    ax,offset bios_kb_buffer    ; if they match, ok so far
  858.     jne    inst_io_err            ; if not, who knows?
  859.  
  860. inst_io_1:
  861.     mov    ax,es:bios_buffer_end        ; check contents of next pointer
  862.     or    ax,ax                ; zero?
  863.     jz    inst_io_2            ; if so, definitely old rom -
  864.                         ;  ok to install
  865.     cmp    ax,offset bios_kb_buffer_end    ; match?  ok to install
  866.                         ;  (new rom but default buffer)
  867.     jne    inst_io_err            ; if not, who knows?
  868.  
  869. inst_io_2:                    ; initialize our keyboard buffer
  870.  
  871.     mov    si,offset our_kb_buffer     ; point SI & head to start
  872.     mov    our_buffer_head,si        ;  of new keyboard buffer
  873.  
  874.     call    movem                ; get chars & setup bios buf
  875.  
  876.     mov    al,kb_io_int_no         ; int number for keyboard i/o
  877.     mov    ah,set_int_vec            ; dos set int. vector function
  878.     mov    dx,offset kb_io_int_hdlr    ; offset of new i/o handler
  879.     int    dosint                ; set the vector
  880.  
  881.     mov    buf_flag,true            ; tell kb_int_hdlr it's ok to
  882.                         ;  use the buffer now..
  883.     clc                    ; clear CF to show no error
  884.     jmp short inst_io_exit            ; return
  885.  
  886. inst_io_err:                    ; unknown buffer condition found
  887.                         ;  - don't install
  888.     mov    dx,offset buf_err_msg        ; print informational messages..
  889.     call    msg                ;
  890.     stc                    ; set CF to show error
  891.  
  892. inst_io_exit:
  893.     pop    es
  894.     ret
  895.  
  896. ; get existing chars into our buffer - SI points to beginning of our buffer
  897.  
  898. movem:
  899.     mov    bx,es:bios_buffer_head        ; get ptr to 1st char in buf
  900. movem1:
  901.     cmp    bx,es:bios_buffer_tail        ; if tail = head,
  902.     je    movem_exit            ;  nothing there - setup buf
  903.     mov    ax,es:[bx]            ; else, get a word of data
  904.     mov    [si],ax             ; put it in our buffer
  905.     inc    bx                ; bump both ptrs to next word
  906.     inc    bx                ;
  907.     inc    si                ;
  908.     inc    si                ;
  909.     cmp    bx,offset bios_kb_buffer_end    ; at end of bios buffer?
  910.     jne    movem2                ; no, continue
  911.     mov    bx,offset bios_kb_buffer    ; set to beginning of buffer
  912. movem2:
  913.     jmp    movem1                ; loop while chars available
  914.  
  915. movem_exit:
  916.     mov    our_buffer_tail,si        ; store ptr to next pos. avail.
  917.     call    init_bios_buf            ; set keyboard buf to empty
  918.     ret
  919.  
  920. install_kb_io_int_hdlr    endp
  921.  
  922.  
  923. ;**************************************************************************
  924. ;* print msg (offset in dx)
  925.  
  926. msg proc near
  927.  
  928.     push    ax                ;
  929.     mov    ah,print_string         ; dos function number
  930.     int    dosint                ;
  931.     pop    ax                ;
  932.     ret
  933.  
  934. msg endp
  935.  
  936. ;**************************************************************************
  937. ;*
  938. ;*    GET_VEC     get interrupt vector
  939. ;*
  940. ;*    Entry:        AL = interrupt number
  941. ;*
  942. ;*    Exit:        ES:BX contain CS:IP for interrupt vector
  943. ;*
  944. ;*    (Only ES and BX changed)
  945. ;*
  946. ;**************************************************************************
  947.  
  948. get_vec proc near
  949.  
  950.     push    ax            ; save entering value
  951.     push    ax            ; save interrupt number requested
  952.     mov    ah,get_vers        ; dos get version function
  953.     int    dosint            ; major version returned in AL, minor in AH
  954.     pop    bx            ; restore int. number requested
  955.     cmp    al,2            ; less than 2 = pre dos 2.0
  956.     jb    gv_direct        ; get vector directly
  957.  
  958.     mov    al,bl            ; move requested interrupt vector into AL
  959.     mov    ah,get_int_vec        ; dos get int. vector function
  960.     int    dosint            ; CS:IP returned in ES:BX
  961.     pop    ax            ;
  962.     ret
  963.  
  964. gv_direct:                ; get vector directly
  965.                     ; first, convert number in BL to offset in BX
  966.     xor    bh,bh            ; make sure BH = 0
  967.     shl    bx,1            ; multiply by 4
  968.     shl    bx,1            ;
  969.     mov    ax,int_vecs        ; point ES to segment for int. vectors
  970.     mov    es,ax            ;
  971.     mov    ax,es:[bx+2]        ; get CS value into AX
  972.     mov    bx,es:[bx]        ; get IP value into BX
  973.     mov    es,ax            ; mov CS value into ES
  974.     pop    ax            ;
  975.     ret
  976.  
  977. get_vec endp
  978.  
  979. ;****************************************************************
  980. ;*
  981. ;*    get_parms    scan command line for /b or /n parm
  982. ;*
  983. ;*    exit:    carry set on error
  984. ;*
  985. ;*    AX,BX,SI changed
  986. ;*
  987. ;****************************************************************
  988.  
  989. get_parms    proc near
  990.  
  991.     mov    bx,offset cmd_ct    ; BX pts to number of cmd line chars
  992.     mov    si,[bx]         ; get count
  993.     inc    bx            ; point BX to 1st char
  994.     and    si,0ffh         ; max. 127 chars. - set flags
  995.     jz    gp_err_exit        ; no parms, skip the rest
  996.     mov    byte ptr [bx][si],0    ; make command line null terminated
  997.  
  998. gp_top_loop:
  999.     mov    al,[bx]         ; get the char
  1000.     or    al,al            ; null?
  1001.     je    gp_err_exit        ; if so, return error
  1002.     cmp    al,spc            ; space?
  1003.     je    gp_bot_loop        ; yes, get another char
  1004.     cmp    al,tab            ; tab?
  1005.     je    gp_bot_loop        ; yes, get another char
  1006.     cmp    al,'/'                  ; see if possible /b or /n switch
  1007.     jne    gp_err_exit        ; no, return error
  1008.     call    ck_switches        ; check parm, carry set on error
  1009.     jc    gp_err_exit        ; carry set, return error
  1010.     jmp    gp_exit         ;
  1011.  
  1012. gp_bot_loop:
  1013.     inc    bx            ; point to next char
  1014.     jmp    gp_top_loop        ; go get it
  1015.  
  1016. gp_err_exit:
  1017.     stc                ; set carry to show error
  1018. gp_exit:                ; if got here by jump, carry is reset
  1019.     ret
  1020.  
  1021. ck_switches:                ; ck parms ( BX pts to '/' )
  1022.     inc    bx            ; point to possible switch
  1023.     mov    al,[bx]         ; get char
  1024.     cmp    al,'Z'                  ; check if possibly upper-case
  1025.     jbe    ck_sw1            ; no? continue
  1026.     sub    al, 'a' - 'A'           ; make upper-case
  1027. ck_sw1:
  1028.     cmp    al,'B'                  ; buffer desired ?
  1029.     jne    ck_sw2            ; no? continue
  1030.     mov    buf_desired,true    ; else, set flag
  1031.     jmp    ck_sw_exit        ; and exit
  1032. ck_sw2:
  1033.     cmp    al,'N'                  ; no buffer desired ?
  1034.     je    ck_sw_exit        ; yes, return ok
  1035.     stc                ; set carry to show error
  1036. ck_sw_exit:
  1037.     ret
  1038.  
  1039. get_parms    endp
  1040.  
  1041. cseg    ends
  1042.  
  1043. end    entry
  1044.